# coding:utf-8

import sys
import os

import Config
import IniFile
import Global
import Loading
import ELog


# matplotlib采用WXAgg为后台,将matplotlib嵌入wxPython中  
# matplotlib.use("WXAgg")

try:
    from agw import fourwaysplitter as FWS
except ImportError:  # if it's not there locally, try the wxPython lib.
    try:
        import wx.lib.agw.fourwaysplitter as FWS
    except ImportError:
        exit()

# 菜单ID
from AppIds import *

import SerialUtil
import wx.lib.newevent

# Frame start
SERIAL_GENIUS_FS_CHAR = 0x0F
# Slash
SERIAL_GENIUS_SL_CHAR = 0x1F


# 主要的4个窗口
class PanelMain(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, id=ID_WINDOW_PANEL_MAIN)

        self.splitter = FWS.FourWaySplitter(self, agwStyle=wx.SP_LIVE_UPDATE)
        self.splitter.SetAGWWindowStyleFlag(wx.SP_LIVE_UPDATE)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.splitter, 1, wx.EXPAND)
        self.SetSizer(sizer)
        self.windowShowIndexs = []

        # 动态导入所有的模块并加载
        self.modules = []

        for mod in Global.Modules:
            try:
                exec 'import module.' + mod
                exec 'self.modules.append(module.' + mod + '.' + (mod.capitalize()) + '(self.splitter))'
            except:
                ELog.Trace()
            pass

        # 初始化模块
        for mod in self.modules:
            mod.Hide()
            mod.isUsing = False

        flag = False
        for i in range(0, len(self.modules)):
            if Config.APP_CONFIG['setting']['show_modules'].find('[' + str(i) + ']') >= 0:
                self.UseModule(i, True)
                flag = True

        # 如果没有预设的窗口，显示0 1 2 3
        if not flag:
            self.UseModule(0, True)
            self.UseModule(1, True)
            self.UseModule(2, True)
            self.UseModule(3, True)

    # 返回已使用的模块数
    def UsedModuleNumber(self):
        return len(self.windowShowIndexs)

    # 设置模块可用与否
    def UseModule(self, index, use):
        if index < 0 or index > len(self.modules) - 1:
            return
        if use:
            if self.UsedModuleNumber() >= 4:
                return
            findWindow = False
            for i in range(0, len(self.windowShowIndexs)):
                if self.windowShowIndexs[i] == index:
                    findWindow = True
            if not findWindow:
                self.windowShowIndexs.append(index)
                Global.var['PanelTool'].checkboxLayouts[index].SetValue(True)

                self.splitter.AppendWindow(self.modules[index])
                self.modules[index].Show()
                self.modules[index].isUsing = True
        else:
            if self.UsedModuleNumber() <= 1:
                return
            findWindow = False
            findIndex = 0
            for i in range(0, len(self.windowShowIndexs)):
                if self.windowShowIndexs[i] == index:
                    findIndex = i
                    findWindow = True

            if findWindow:
                del self.windowShowIndexs[findIndex]
                Global.var['PanelTool'].checkboxLayouts[index].SetValue(False)

                self.splitter.DetachWindow(self.modules[index])
                self.modules[index].Hide()
                self.modules[index].isUsing = False

        if self.UsedModuleNumber() == 1:
            self.splitter.SetExpanded(0)
        elif self.UsedModuleNumber() == 2:
            self.splitter.SetExpanded(-1)
            self.splitter.SetVSplit(10000)
        else:
            self.splitter.SetExpanded(-1)
            self.splitter.SetVSplit(5000)


# 左侧工具栏窗口
class PanelTool(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, id=ID_WINDOW_PANEL_TOOL)

        sizer = wx.BoxSizer(wx.VERTICAL)

        # 串口
        ports = ["None"]
        for port in SerialUtil.GetValidPort():
            ports.append('COM' + str(port + 1))

        sizer.Add(wx.StaticText(self, -1, u"串口"), 0, wx.ALL, 2)
        self.comboPort = wx.ComboBox(self, -1, choices=ports,
                                     style=wx.CB_READONLY | wx.CB_DROPDOWN)

        if Config.APP_CONFIG['setting']['default_port'] in ports:
            self.comboPort.SetStringSelection(Config.APP_CONFIG['setting']['default_port'])
        else:
            self.comboPort.SetStringSelection("None")

        sizer.Add(self.comboPort, 0, wx.ALL, 2)

        # 刷新
        self.btnRefreshPort = wx.Button(self, 0, u"刷新", size=(50, -1))
        self.btnRefreshPort.Bind(wx.EVT_BUTTON, self.OnRefreshPort)
        self.btnRefreshPort.SetSize(self.btnRefreshPort.GetBestSize())
        sizer.Add(self.btnRefreshPort, 0, wx.ALL, 2)

        # 波特率
        sizer.Add(wx.StaticText(self, -1, u"波特率"), 0, wx.ALL, 2)
        baudrates = []
        for rate in SerialUtil.BAUDRATES:
            baudrates.append(str(rate))
        self.comboBaudrate = wx.ComboBox(self, -1, choices=baudrates,
                                         style=wx.CB_READONLY | wx.CB_DROPDOWN)

        if Config.APP_CONFIG['setting']['default_baudrate'] in baudrates:
            self.comboBaudrate.SetStringSelection(Config.APP_CONFIG['setting']['default_baudrate'])
        else:
            self.comboBaudrate.SetStringSelection("9600")

        sizer.Add(self.comboBaudrate, 0, wx.ALL, 2)

        # 数据位
        sizer.Add(wx.StaticText(self, -1, u"数据位"), 0, wx.ALL, 2)
        bytesizes = list(SerialUtil.BYTESIZES)
        default_bytesize = None
        for i in range(0, len(bytesizes)):
            bytesizes[i] = str(bytesizes[i])
            if Config.APP_CONFIG['setting']['default_bytesize'] == bytesizes[i]:
                default_bytesize = bytesizes[i]
        bytesizes.sort()
        self.comboBytesize = wx.ComboBox(self, -1, choices=bytesizes,
                                         style=wx.CB_READONLY | wx.CB_DROPDOWN)

        if default_bytesize:
            self.comboBytesize.SetStringSelection(default_bytesize)
        else:
            self.comboBytesize.SetStringSelection(bytesizes[0])

        sizer.Add(self.comboBytesize, 0, wx.ALL, 2)

        # 停止位
        sizer.Add(wx.StaticText(self, -1, u"停止位"), 0, wx.ALL, 2)
        stopbits = list(SerialUtil.STOPBITS)
        default_stopbit = None
        for i in range(0, len(stopbits)):
            stopbits[i] = str(stopbits[i])
            if Config.APP_CONFIG['setting']['default_stopbits'] == stopbits[i]:
                default_stopbit = stopbits[i]
        stopbits.sort()
        self.comboStopbits = wx.ComboBox(self, -1, choices=stopbits,
                                         style=wx.CB_READONLY | wx.CB_DROPDOWN)

        if default_stopbit:
            self.comboStopbits.SetStringSelection(default_stopbit)
        else:
            self.comboStopbits.SetStringSelection(stopbits[0])

        sizer.Add(self.comboStopbits, 0, wx.ALL, 2)

        # 校验
        sizer.Add(wx.StaticText(self, -1, u"校验"), 0, wx.ALL, 2)
        paritys = []
        default_parity = None
        for key, value in SerialUtil.PARITY_NAMES.items():
            paritys.append(value)
            if Config.APP_CONFIG['setting']['default_parity'] == value:
                default_parity = value
        paritys.sort()
        self.comboParity = wx.ComboBox(self, -1, choices=paritys,
                                       style=wx.CB_READONLY | wx.CB_DROPDOWN)

        if default_bytesize:
            self.comboParity.SetStringSelection(default_parity)
        else:
            self.comboParity.SetStringSelection(paritys[0])

        sizer.Add(self.comboParity, 0, wx.ALL, 2)

        # 打开
        sizer.Add(wx.StaticText(self, -1, u""), 0, wx.ALL, 2)  # 占位
        self.btnOpenPort = wx.Button(self, 0, u"打开")
        self.btnOpenPort.Bind(wx.EVT_BUTTON, self.OnOpenPort)
        sizer.Add(self.btnOpenPort, 0, wx.ALL, 2)

        # 显示所有模块
        self.checkboxLayouts = []
        for modname in Global.ModulesName:
            cb = wx.CheckBox(self, -1, modname)
            sizer.Add(cb, 0, wx.ALL, 2)
            self.checkboxLayouts.append(cb)
            cb.Bind(wx.EVT_CHECKBOX, self.OnLayoutNumberChange)

        # 意见反馈
        try:
            try:
                from agw import hyperlink as hl
            except ImportError:
                import wx.lib.agw.hyperlink as hl
            sizer.Add(wx.StaticText(self, -1, u""), 0, wx.ALL, 2)  # 占位
            sizer.Add(hl.HyperLinkCtrl(self, -1, u"意见反馈", URL=Global.app['suggestion_url']))
        except:
            pass

        # 设置布局
        border = wx.BoxSizer()
        border.Add(sizer, 1, wx.EXPAND | wx.ALL, 5)
        self.SetSizer(border)

    # 更改显示模块
    def OnLayoutNumberChange(self, event):
        passed = True
        if event.GetEventObject().GetValue():
            if Global.var['PanelMain'].UsedModuleNumber() >= 4:
                wx.MessageBox(u'最多只能显示4个')
                passed = False
        else:
            if Global.var['PanelMain'].UsedModuleNumber() <= 1:
                wx.MessageBox(u'至少选择一个')
                passed = False

        if passed:
            Config.APP_CONFIG['setting']['show_modules'] = ''
            for i in range(0, len(self.checkboxLayouts)):
                Global.var['PanelMain'].UseModule(i, self.checkboxLayouts[i].GetValue())
                if self.checkboxLayouts[i].GetValue():
                    Config.APP_CONFIG['setting']['show_modules'] += '[' + str(i) + ']'
        else:
            event.GetEventObject().SetValue(not event.GetEventObject().GetValue())

        Global.var['PanelMain'].splitter.Refresh()
        Global.var['PanelMain'].Refresh()
        Global.var['PanelTool'].Refresh()

    # 打开串口
    def OnOpenPort(self, event, forceClose=False):
        if (Global.SerialGeniusPort and Global.SerialGeniusPort.running) or forceClose:
            Global.SerialGeniusPort.stop()
            self.btnOpenPort.SetLabel(u'打开')
            self.comboPort.Enable(True)
            self.btnRefreshPort.Enable(True)
            self.comboBaudrate.Enable(True)
            self.comboBytesize.Enable(True)
            self.comboStopbits.Enable(True)
            self.comboParity.Enable(True)
            return

        if self.comboPort.GetCurrentSelection() <= 0:
            wx.MessageBox(u"请选择串口", u"提示")
            return

        try:
            Config.APP_CONFIG['setting']['default_port'] = self.comboPort.GetStringSelection()
            t_port = int(Config.APP_CONFIG['setting']['default_port'][3:]) - 1

            Config.APP_CONFIG['setting']['default_baudrate'] = self.comboBaudrate.GetStringSelection()
            t_baudrate = int(Config.APP_CONFIG['setting']['default_baudrate'])

            Config.APP_CONFIG['setting']['default_bytesize'] = self.comboBytesize.GetStringSelection()
            t_bytesize = int(Config.APP_CONFIG['setting']['default_bytesize'])

            Config.APP_CONFIG['setting']['default_parity'] = self.comboParity.GetStringSelection()
            t_parity = Config.APP_CONFIG['setting']['default_parity']

            for key, value in SerialUtil.PARITY_NAMES.items():
                if t_parity == value:
                    t_parity = key
                    break

            Config.APP_CONFIG['setting']['default_stopbits'] = self.comboStopbits.GetStringSelection()
            t_stopbits = float(Config.APP_CONFIG['setting']['default_stopbits'])
            if t_stopbits == 1.0:
                t_stopbits = 1
            elif t_stopbits == 2.0:
                t_stopbits = 2
        except:
            wx.MessageBox(u"打开串口失败:0x01", u"提示")
            ELog.Trace()
            return

        t_timeout = 2
        # print t_port, t_baudrate, t_bytesize, t_parity, t_stopbits, t_timeout

        Global.SerialGeniusPort = SerialUtil.SerialThread(port=t_port,
                                                          baudrate=t_baudrate,
                                                          bytesize=t_bytesize,
                                                          parity=t_parity,
                                                          stopbits=t_stopbits,
                                                          timeout=t_timeout)

        if False == Global.SerialGeniusPort.start():
            wx.MessageBox(u"打开串口失败:0x02", u"提示")
            return
        else:
            self.comboPort.Enable(False)
            self.btnRefreshPort.Enable(False)
            self.comboBaudrate.Enable(False)
            self.comboBytesize.Enable(False)
            self.comboStopbits.Enable(False)
            self.comboParity.Enable(False)
            self.btnOpenPort.SetLabel(u'关闭')

    # 刷新串口
    def OnRefreshPort(self, event):
        Loading.Show()
        self.comboPort.Clear()
        self.comboPort.Append("None")
        for port in SerialUtil.GetValidPort():
            self.comboPort.Append('COM' + str(port + 1))
        self.comboPort.SetStringSelection("None")
        Loading.Hide()


# 布局Panel
class PanelLayout(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        sizer = wx.BoxSizer(wx.HORIZONTAL)

        Global.var['PanelTool'] = PanelTool(self)
        sizer.Add(Global.var['PanelTool'])

        Global.var['PanelMain'] = PanelMain(self)
        sizer.Add(Global.var['PanelMain'], 1, wx.EXPAND)

        self.SetSizer(sizer)


# 主框架
class MainFrame(wx.Frame):
    def __init__(self, parent, title):
        # 获取系统环境信息
        self._EnvironmentInit()

        wx.Frame.__init__(self,
                          parent,
                          id=-1,
                          title=title,
                          pos=wx.DefaultPosition,
                          size=(800, 600),
                          style=wx.DEFAULT_FRAME_STYLE | wx.SUNKEN_BORDER | wx.CLIP_CHILDREN)

        # 窗口初始化
        self._WindowInit()

        # 初始化状态栏
        self._StatusBarInit()
        # self._UpdateStatusBar(u"正在加载...")
        self._UpdateStatusBar(u"欢迎使用SerialGenius！")

        # 设置Icon
        try:
            self.icon = wx.Icon(Global.env['app_root'] + 'dll/ico.dll', wx.BITMAP_TYPE_ICO)
            self.SetIcon(self.icon)
        except:
            ELog.Trace()

        # 设置布局
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(PanelLayout(self), 1, wx.EXPAND)
        self.SetSizer(sizer)
        sizer.Layout()

        # startup window
        try:
            from agw import advancedsplash as AS
        except ImportError:  # if it's not there locally, try the wxPython lib.
            import wx.lib.agw.advancedsplash as AS
        try:
            frame = AS.AdvancedSplash(self,
                                      bitmap=wx.Bitmap(Global.env['app_root'] + "dll/su.dll", wx.BITMAP_TYPE_PNG),
                                      timeout=3000,
                                      agwStyle=AS.AS_TIMEOUT | AS.AS_CENTER_ON_SCREEN)
            # remove default click close handler
            frame.Bind(wx.EVT_LEFT_DOWN, self.OnYummy)
            frame.Bind(wx.EVT_LEFT_UP, self.OnYummy)
            frame.Bind(wx.EVT_RIGHT_DOWN, self.OnYummy)
            frame.Bind(wx.EVT_RIGHT_UP, self.OnYummy)
        except:
            pass

        # version check
        import Version
        Version.AutoCheckVersion()

    def OnYummy(self, event):
        pass

        ################# Private #################

    # 环境初始化
    def _EnvironmentInit(self):
        mm = wx.DisplaySize()
        Global.env['screen']['width'] = mm[0]
        Global.env['screen']['height'] = mm[1]
        root = os.path.dirname(os.path.abspath(sys.argv[0]))
        if root[-1:] != os.sep:
            root = root + os.sep
        Global.env['app_root'] = root

    # 窗口初始化
    def _WindowInit(self):
        # 设置最小显示
        self.SetMinSize(wx.Size(400, 300))
        # 关闭提示
        self.Bind(wx.EVT_CLOSE, self._OnClose)
        # 在中间显示
        self.CenterOnScreen()

    # 状态栏初始化
    def _StatusBarInit(self):
        self._statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
        self._statusbar.SetStatusWidths([-1, 300])
        self._statusbar.SetStatusText(Config.APP_CONFIG['copyright'], 1)

    def _SafetyCloseWindow(self, event):
        # 如果串口已打开，尝试关闭
        if (Global.SerialGeniusPort and Global.SerialGeniusPort.running):
            Global.SerialGeniusPort.stop()

            # 保存当前配置
        IniFile.Save()

        if event == None:
            self.Destroy()
        else:
            event.Skip()

    ################# Public #################
    # 改变状态栏
    def _UpdateStatusBar(self, text):
        self._statusbar.SetStatusText(text, 0)

    ############## Windows Event #############
    def _OnClose(self, event):
        self._SafetyCloseWindow(event)


class MainApp(wx.App):
    def OnInit(self):
        # 加载配置
        IniFile.Load()
        Global.var['Frame'] = MainFrame(None, Config.APP_CONFIG['name'] + ' V' + Config.APP_CONFIG['version'] + ' - ' +
                                        Config.APP_CONFIG['description'])

        self.SetTopWindow(Global.var['Frame'])

        Global.var['Frame'].Show(True)
        return True


if __name__ == '__main__':
    app = MainApp()
    app.MainLoop()
